﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Xml;

using System.IO;

using System.Drawing;
using System.Drawing.Drawing2D;
using System.ComponentModel;
using Athena.Core;

namespace Athena.Objects
{
    [Serializable()]
    public class ContainerGlyph : IGlyph
    {
        protected bool _Glow = false;

        public override object Clone()
        {
            return new ContainerGlyph(this);
        }

        public override void RenderGlyph(Graphics g)
        {
        }

        public ContainerGlyph(ContainerGlyph copy)
        {
            _dEdgeThickness = copy._dEdgeThickness;
            _Dimensions = copy._Dimensions;
            _oEdgeColor = copy._oEdgeColor;
            _oFillColorStart = copy._oFillColorStart;
            _oFillColorEnd = copy._oFillColorEnd;
            _oShape = copy._oShape;

            int items = copy._Drawables.Count;
            _Drawables = new List<IDrawable>(items);

            //Item =new Module(copy.Item.GetItemType(), copy.Item.GetID(), inputs, outputs, items);
            Text = copy.Text;

            //cloning the edges requires keeping track of duplicated nodes

            List<IDrawable> originals = new List<IDrawable>(),
                            duplicates = new List<IDrawable>();

            //clone the nodes

            for (int i = 0; i < copy._Drawables.Count; ++i)
            {
                if (!(copy._Drawables[i] is Connector))
                {
                    IDrawable temp = (IDrawable)copy._Drawables[i].Clone();
                    temp.Location = Utility.SubtractPoints(temp.Location, copy.Location);

                    originals.Add((IDrawable)copy._Drawables[i]);   //keep record: duplicate[i] = copy of original[i]
                    duplicates.Add(temp);

                    _Drawables.Add(temp);         //add each node to the items
                    temp.Parent = this;

                }
            }

            //now we have copied all nodes and links and have a list of original-duplicate pairs

            //set left and right
            #region set left and right

            for (int i = 0; i < originals.Count; ++i)
            {
                if ((originals[i] is PartGlyph) &&
                    (duplicates[i] is PartGlyph)) //for each part
                {

                    IDrawable left = null;
                    if (copy._Drawables[i] is PartGlyph)
                        left = ((PartGlyph)copy._Drawables[i]).Left;

                    if (left != null)
                    {
                        for (int j = 0; j < originals.Count; ++j)
                        {
                            if (originals[j] == left)
                            {
                                if (duplicates[i] is PartGlyph)
                                    ((PartGlyph)duplicates[i]).Left = duplicates[j] as PartGlyph;
                            }
                        }
                    }
                    IDrawable right = null;
                    if (copy._Drawables[i] is PartGlyph)
                        right = ((PartGlyph)copy._Drawables[i]).Right;

                    if (right != null)
                    {
                        for (int j = 0; j < originals.Count; ++j)
                        {
                            if (originals[j] == right)
                            {
                                if (duplicates[i] is PartGlyph)
                                    ((PartGlyph)duplicates[i]).Right = (PartGlyph)duplicates[j];
                            }
                        }
                    }
                }
            }

            #endregion

            //make connections
            #region making connectors
            int numConnectors = 0;
            for (int i = 0; i < copy._Drawables.Count; ++i)
            {
                if (copy._Drawables[i] is Connector)   //for each edge
                {
                    ++numConnectors;

                    List<IDrawable> originalInputNodes = ((Connector)copy._Drawables[i]).Reactants; //get its inputs
                    List<IDrawable> originalOutputNodes = ((Connector)copy._Drawables[i]).Products; //get its outputs

                    if (originalInputNodes != null && originalOutputNodes != null)
                    {
                        List<IDrawable> duplicateInputNodes = new List<IDrawable>(originalInputNodes.Count);
                        List<IDrawable> duplicateOutputNodes = new List<IDrawable>(originalOutputNodes.Count);

                        foreach (IDrawable n in originalInputNodes)    //for each input node in this edge
                        {
                            for (int j = 0; j < originals.Count; ++j)   //find the index of
                            {
                                if (n == originals[j])                   //the original node
                                {
                                    duplicateInputNodes.Add(duplicates[j]);  //add the corresponding duplicate node to the new inputs list
                                    break;
                                }
                            }
                        }

                        foreach (IDrawable n in originalOutputNodes)    //for each output node in this edge
                        {
                            for (int j = 0; j < originals.Count; ++j)   //find the index of
                            {
                                if (n == originals[j])                   //the original node
                                {
                                    duplicateOutputNodes.Add(duplicates[j]);  //add the corresponding duplicate node to the new output list
                                    break;
                                }
                            }
                        }

                        //make a new edge using the correct duplicates and the original rate law
                        BezierConnector connector = new BezierConnector(duplicateInputNodes, duplicateOutputNodes);
                        if (connector.Item is Edge && copy._Drawables[i].Item is Edge && !copy._Drawables[i].NullItem)
                        {
                            Edge original = (Edge)(copy._Drawables[i].Item);
                            Edge edge = (Edge)connector.Item;
                            edge.SetReactantsStoichiometry(original.Reactants, original.ReactantsStoichiometry);
                            edge.SetProductsStoichiometry(original.Products, original.ProductsStoichiometry);
                            edge.Rate = original.Rate;
                            edge.SetModifiersStoichiometry(original.Modifiers, original.ModifierStoichiometry);
                            edge.ItemType = original.ItemType;
                        }
                        /*
                        if (copy._Drawables[i] is BezierConnector)
                        {
                            BezierConnector bz = (BezierConnector)copy._Drawables[i];

                            List<BezierConnector.CurveSegment> visited = new List<BezierConnector.CurveSegment>();

                            PointF point = Utility.SubtractPoints(copy.Location, bz.Location);

                            foreach (BezierConnector.CurveSegment curve in connector.CurveSegments)
                            {
                                if (!visited.Contains(curve))
                                {
                                    foreach (BezierConnector.CurveSegment curve2 in bz.CurveSegments)
                                    {
                                        if (curve.StartNode.ID == curve2.StartNode.ID)
                                        {
                                            curve.CopyPointsFrom(curve2);
                                            curve.Translate(point);
                                            visited.Add(curve);
                                            break;
                                        }
                                    }
                                }
                            }
                        }
                        */
                        _Drawables.Add(connector);
                        connector.Parent = this;
                    }
                }
            }
            #endregion

            foreach (IDrawable d in copy.AnchoredItems)
            {
                IDrawable oNew = (IDrawable)d.Clone();
                oNew.Location = new PointF(d.Location.X - copy.Location.X + Location.X,
                            d.Location.Y - copy.Location.Y + Location.Y);
                oNew.AnchoredSource = this;
                oNew.DoneModifying(Corner.None, true);
            }

            Location = copy._Location;
            _RespondsToCollision = true;
        }

        protected List<IDrawable> _Drawables = new List<IDrawable>();

        /// <summary>
        /// The complete constructor of a PartGlyph
        /// </summary>
        /// <param name="location"></param>
        public ContainerGlyph(PointF location, SizeF dimension, List<IDrawable> items)
        {
            _Location = location;
            _Dimensions = dimension;
            Name = "Module";
            _oFillColorStart = Color.Beige;
            _oFillColorEnd = Color.Beige;
            _oEdgeColor = Color.LightGray;

            _Drawables = items;
            _RespondsToCollision = true;
        }

        /// <summary>
        /// Initializes a new instance of the PartGlyph class.
        /// </summary>
        /// <param name="location"></param>
        public ContainerGlyph(PointF location)
        {
            Location = new PointF(location.X - 25, location.Y - 25);
            _OldLocation = _Location;
            _Dimensions = new SizeF(50, 50);
            _oFillColorStart = Color.BlueViolet;
            _oFillColorEnd = Color.BlueViolet;
            _dEdgeThickness = 5f;
            _oEdgeColor = Color.Black;
            _Drawables = new List<IDrawable>();
            _RespondsToCollision = true;
        }

        public ContainerGlyph()
        {
            _Location = new PointF(0, 0);
            _Dimensions = new SizeF(50, 50);
            _oFillColorStart = Color.BlueViolet;
            _oFillColorEnd = Color.BlueViolet;
            _dEdgeThickness = 5f;
            _oEdgeColor = Color.Black;
            _Drawables = new List<IDrawable>();
            _RespondsToCollision = true;
        }

        public enum SHAPE
        {
            Box,
            Ellipse,
            RoundedRectangle,
            Octahedron

        }

        protected SHAPE _oShape = SHAPE.Box;

        [Description("Allows to choose the Shape drawn for this SimpleGlyph"), Category("PartGlyph")]
        public virtual SHAPE Shape
        {
            get { return _oShape; }
            set { _oShape = value; }
        }

        public override bool HasPreview
        {
            get { return false; }
        }

        public override Bitmap GetPreview(int width, int height)
        {
            Bitmap oBitmap = new Bitmap(width, height);
            Graphics g = Graphics.FromImage(oBitmap);

            PointF oldLocation = _Location;
            SizeF oldDimensions = _Dimensions;
            float oldEdgeThickness = _dEdgeThickness;

            _Location = new PointF(1f, 1f);
            _dEdgeThickness = 1.5f;
            _Dimensions = new SizeF(width - 3 * _dEdgeThickness, height - 3 * _dEdgeThickness);
            RenderGlyph(g);

            _Location = oldLocation;
            _Dimensions = oldDimensions;
            _dEdgeThickness = oldEdgeThickness;

            return oBitmap;
        }

        public override bool IntersectsWith(RectangleF rect)
        {
            RectangleF oTemp = Bounds;
            oTemp.Inflate(-2 * _dEdgeThickness, -2 * _dEdgeThickness);
            if (oTemp.Contains(rect))
                return false;
            else
                return base.IntersectsWith(rect);
        }

        public override PointF Location
        {
            get
            {
                return _Location;
            }
            set
            {
                PointF offset = new PointF(value.X - _Location.X, value.Y - _Location.Y);
                _Location = value;

                /*if (Parent != null)
                {
                    Parent.ChildChanged(this);
                }*/
                foreach (IDrawable id in _AnchoredItems)
                {
                    if (!id.IsSelected && !_Drawables.Contains(id))
                        id.MoveTo(Utility.AddPoints(id.Location, offset));
                }

                if (_Drawables == null) return;

                foreach (IDrawable id in _Drawables)
                {
                    if (!id.IsSelected && !(id.AnchoredSource != null && _Drawables.Contains(id.AnchoredSource)))
                        id.MoveTo(Utility.AddPoints(id.Location, offset));
                }

            }
        }

        public override void MoveTo(PointF newPoint)
        {
            Location = newPoint;
        }

        public override bool ChildChanged(IDrawable child)
        {
            if (!(Bounds.Contains(child.Bounds)))
                RemoveItem(child);
            return true;
        }

        public void AddItem(IDrawable item)
        {
            AddItem(item, true);
        }

        public virtual void AddItem(IDrawable item, bool adjustSize)
        {
            if (item == null || NullItem || _Drawables.Contains(item) || _AnchoredItems.Contains(item)) return;

            //if (item.Parent != null && item.Parent != this) item.Parent.RemoveChild(item);
            if (_Drawables == null) _Drawables = new List<IDrawable>();

            if (item.Parent != this)
            {
                item.Parent = this;  //VERY important -- this is where Item is also updated
            }

            if (_Drawables.Contains(item)) return;

            _Drawables.Add(item);

            if (item is PartGlyph)
            {
                PartGlyph part = ((PartGlyph)item);
                foreach (PartGlyph p in part.ConnectedParts)
                {
                    if (!_Drawables.Contains(p) && Bounds.Contains(p.Bounds))
                    {
                        _Drawables.Add(p);
                        if (p.Parent != this)
                            p.Parent = this;
                    }
                }
                part.UpdateLocation();
                if (adjustSize) AdjustSize();
            }
            else
            {
                if (adjustSize)
                    AdjustSize(item);  //time saver
            }

            //Utility.BringItemToFront(item, Program.Model);
            Utility.MoveBehindOf(this, item, Program.Model);
            //Utility.Refresh();
        }

        public virtual void RemoveItem(IDrawable item)
        {
            if (_Drawables == null) _Drawables = new List<IDrawable>();
            _Drawables.Remove(item);
            if (item.Parent == this)
            {
                item.Parent = null;
                if (!Program.Model.Drawables.Contains(item))
                    Program.Model.Drawables.Add(item);
            }
            if (item is PartGlyph)
            {
                List<PartGlyph> parts = ((PartGlyph)item).ConnectedParts;
                foreach (PartGlyph p in parts)
                {
                    if (!Bounds.Contains(p.Bounds))
                    {
                        _Drawables.Remove(p);
                        if (p.Parent == this)
                            p.Parent = null;
                        if (!Program.Model.Drawables.Contains(p))
                            Program.Model.Drawables.Add(p);
                    }
                }
            }
            //item.Location = Utility.AddPoints(item.Location, _Location);
        }


        /// <summary>
        /// Remove this instance from the model
        /// </summary>
        public override void Remove()
        {
            bool nullItem = NullItem;
            base.Remove();
            if (_Drawables != null || _Drawables.Count > 1)
            {
                for (int i = _Drawables.Count - 1; i >= 0; --i)
                {
                    if (i >= _Drawables.Count) i = _Drawables.Count - 1;
                    if (nullItem)
                        RemoveItem(_Drawables[i]);
                    else
                        _Drawables[i].Remove();
                    if (_Drawables.Count == 0) break;
                }
            }
        }

        [CategoryAttribute("Container Attributes"), Description("contained drawables")]
        public virtual List<IDrawable> Drawables
        {
            get { return _Drawables; }
        }

        public virtual void AddDrawablesToModel(Model model)
        {
            if (!model.Drawables.Contains(this)) model.Drawables.Add(this);
            foreach (IDrawable id in _Drawables)
            {
                if (!model.Drawables.Contains(id))
                {
                    model.Drawables.Add(id);
                    if (id.Name == Model.Empty.Name)
                        Model.Empty.AddAlias(id);
                }
                if (id is ContainerGlyph) ((ContainerGlyph)id).AddDrawablesToModel(model);
            }
        }

        public override void HitBy(IDrawable drawable, bool moving)
        {
            if (_Drawables == null || drawable == this) return;
            if (moving)
            {
                _Glow = !Drawables.Contains(drawable) && !IsParentOf(drawable) && Bounds.Contains(drawable.Bounds);
                //Utility.MoveBehindOf(this, drawable, Program.Model);
                return;
            }
            _Glow = false;
            if (_AnchoredItems != null && _AnchoredItems.Contains(drawable)) return;
            if (drawable.Bounds.Width >= Bounds.Width || drawable.Bounds.Height >= Bounds.Height) return;
            if (drawable.Parent != null)
            {
                if (drawable.Parent == this) return;
                if (drawable.Parent.Bounds.Contains(drawable.Bounds) && !(drawable.Parent.Bounds.Contains(Bounds))) return;
            }

            /*if (drawable is Connector)
            {
                ((Connector)drawable).SyncParent();                
            }
            else*/
            if (!_Drawables.Contains(drawable) && Bounds.Contains(drawable.Bounds))  //if hit by an item already in this part
            {
                AddItem(drawable, false);
            }

            if (drawable is PartGlyph)
            {
                List<IDrawable> list = new List<IDrawable>();
                List<PartGlyph> parts = ((PartGlyph)drawable).ConnectedParts;
                foreach (PartGlyph p in parts)
                    list.Add(p);

                foreach (IDrawable id in list)
                {
                    if (id != null && Bounds.Contains(id.Bounds) && !_Drawables.Contains(id))
                    {
                        AddItem(id, false);
                    }
                }
            }

            //BringItemsToFront();

            base.HitBy(drawable, moving);

            Utility.Refresh();
        }
        public override void HitBy(ICollection<IDrawable> drawables, bool moving)
        {
            if (moving)
            {
                return;
            }
            if (_Drawables == null) return;
            drawables.Remove(this);
            if (_AnchoredItems != null)
                foreach (IDrawable id in _AnchoredItems)
                    drawables.Remove(id);

            List<IDrawable> drawables2 = new List<IDrawable>();
            drawables2.AddRange(drawables);
            foreach (IDrawable drawable in drawables)
            {
                if (Bounds.Contains(drawable.Bounds))
                    if (drawable.AnchoredItems != null && drawable.AnchoredItems.Count > 0)
                        drawables2.AddRange(drawable.AnchoredItems);
            }

            foreach (IDrawable drawable in drawables2)
            {
                //if (!(drawable is Connector))
                //{
                if (drawable.Bounds.Width >= Bounds.Width || drawable.Bounds.Height >= Bounds.Height) continue;

                if (drawable.Parent != null)
                {
                    if (drawable.Parent == this) continue;
                    if (drawable.Parent.Bounds.Contains(drawable.Bounds) && !(drawable.Parent.Bounds.Contains(Bounds))) continue;
                }

                if (!_Drawables.Contains(drawable) && Bounds.Contains(drawable.Bounds))  //if hit by an item already in this part
                {
                    AddItem(drawable, false);
                }

                if (drawable is PartGlyph)
                {
                    List<IDrawable> list = new List<IDrawable>();
                    List<PartGlyph> parts = ((PartGlyph)drawable).ConnectedParts;
                    foreach (PartGlyph p in parts)
                        list.Add(p);


                    foreach (IDrawable id in list)
                    {
                        if (id != null && Bounds.Contains(id.Bounds) && !_Drawables.Contains(id))
                        {
                            AddItem(id, false);
                        }
                    }
                }
                //}
            }
            /*foreach (IDrawable drawable in drawables2)
            {
                if (drawable is Connector)
                {
                    ((Connector)drawable).SyncParent();
                }
            }*/

            foreach (IDrawable id in drawables2)
                if (id is Connector && (id.Parent == null || id.Parent == Parent))
                    Utility.MoveBehindOf(this, id, Program.Model);

            Utility.Refresh();
        }
        public override void Select()
        {
            base.Select();
            //BringItemsToFront();
        }

        public virtual void BringItemsToFront()
        {
            List<IDrawable> list = new List<IDrawable>(_Drawables.Count + 1);
            Utility.BringItemToFront(this, Program.Model);
            list.AddRange(_Drawables);

            if (_AnchoredItems != null && _AnchoredItems.Count > 0)
                list.AddRange(_AnchoredItems);


            foreach (IDrawable id in _Drawables)
            {
                if (id is ContainerGlyph)
                {
                    list.Remove(id);
                    ((ContainerGlyph)id).BringItemsToFront();
                }
            }

            Utility.BringItemsToFront(list, Program.Model);
            Utility.Refresh();
        }

        /// <summary>
        /// Makes the argument container into an alias of this container
        /// </summary>
        /// <returns></returns>
        public override IDrawable GenerateAlias()
        {
            ContainerGlyph copy = new ContainerGlyph();

            copy._dEdgeThickness = _dEdgeThickness;
            copy._Dimensions = _Dimensions;
            copy._oEdgeColor = _oEdgeColor;
            copy._oFillColorStart = _oFillColorStart;
            copy._oFillColorEnd = _oFillColorEnd;
            copy._oShape = _oShape;

            int items = _Drawables.Count;
            copy._Drawables = new List<IDrawable>(items);

            copy.Text = Text;

            //cloning the edges requires keeping track of duplicated nodes

            List<IDrawable> originals = new List<IDrawable>(),
                            duplicates = new List<IDrawable>();

            //clone the nodes

            for (int i = 0; i < _Drawables.Count; ++i)
            {
                if (!(_Drawables[i] is Connector))
                {
                    IDrawable temp = (IDrawable)_Drawables[i].GenerateAlias();
                    temp.Location = Utility.SubtractPoints(temp.Location, Location);

                    originals.Add((IDrawable)_Drawables[i]);   //keep record: duplicate[i] = copy of original[i]
                    duplicates.Add(temp);

                    copy._Drawables.Add(temp);         //add each node to the items
                    temp.Parent = copy;
                }
            }

            //now we have copied all nodes and links and have a list of original-duplicate pairs

            //set left and right
            #region set left and right

            for (int i = 0; i < originals.Count; ++i)
            {
                if ((originals[i] is PartGlyph) &&
                    (duplicates[i] is PartGlyph)) //for each part
                {

                    IDrawable left = null;
                    if (_Drawables[i] is PartGlyph)
                        left = ((PartGlyph)_Drawables[i]).Left;

                    if (left != null)
                    {
                        for (int j = 0; j < originals.Count; ++j)
                        {
                            if (originals[j] == left)
                            {
                                if (duplicates[i] is PartGlyph)
                                    ((PartGlyph)duplicates[i]).Left = duplicates[j] as PartGlyph;
                            }
                        }
                    }
                    IDrawable right = null;
                    if (copy._Drawables[i] is PartGlyph)
                        right = ((PartGlyph)_Drawables[i]).Right;

                    if (right != null)
                    {
                        for (int j = 0; j < originals.Count; ++j)
                        {
                            if (originals[j] == right)
                            {
                                if (duplicates[i] is PartGlyph)
                                    ((PartGlyph)duplicates[i]).Right = duplicates[j] as PartGlyph;
                            }
                        }
                    }
                }
            }

            #endregion

            //make connections
            #region making connectors
            int numConnectors = 0;
            for (int i = 0; i < _Drawables.Count; ++i)
            {
                if (_Drawables[i] is Connector)   //for each edge
                {
                    ++numConnectors;

                    List<IDrawable> originalInputNodes = ((Connector)_Drawables[i]).Reactants; //get its inputs
                    List<IDrawable> originalOutputNodes = ((Connector)_Drawables[i]).Products; //get its outputs

                    if (originalInputNodes != null && originalOutputNodes != null)
                    {
                        List<IDrawable> duplicateInputNodes = new List<IDrawable>(originalInputNodes.Count);
                        List<IDrawable> duplicateOutputNodes = new List<IDrawable>(originalOutputNodes.Count);

                        foreach (IDrawable n in originalInputNodes)    //for each input node in this edge
                        {
                            for (int j = 0; j < originals.Count; ++j)   //find the index of
                            {
                                if (n == originals[j])                   //the original node
                                {
                                    duplicateInputNodes.Add(duplicates[j]);  //add the corresponding duplicate node to the new inputs list
                                    break;
                                }
                            }
                        }

                        foreach (IDrawable n in originalOutputNodes)    //for each output node in this edge
                        {
                            for (int j = 0; j < originals.Count; ++j)   //find the index of
                            {
                                if (n == originals[j])                   //the original node
                                {
                                    duplicateOutputNodes.Add(duplicates[j]);  //add the corresponding duplicate node to the new output list
                                    break;
                                }
                            }
                        }

                        //make a new edge using the correct duplicates and the original rate law
                        Connector connector = new BezierConnector(duplicateInputNodes, duplicateOutputNodes, false);
                        _Drawables[i].AddAlias(connector);
                        connector.Item = _Drawables[i].Item;

                        copy._Drawables.Add(connector);
                        connector.Parent = copy;
                    }
                }
            }
            #endregion

            for (int i = 0; i < AnchoredItems.Count; ++i)
            {
                IDrawable oNew = (IDrawable)AnchoredItems[i].GenerateAlias();
                oNew.Location = new PointF(AnchoredItems[i].Location.X - Location.X + copy.Location.X,
                            AnchoredItems[i].Location.Y - Location.Y + copy.Location.Y);
                oNew.AnchoredSource = copy;
                oNew.DoneModifying(Corner.None, true);
            }

            copy.Location = Location;

            AddAlias(copy);

            Utility.MoveBehindOf(copy, copy.Drawables, Program.Model);

            return copy;
        }
        /*
        public override List<IDrawable> LinkedDrawables
        {
            get
            {
                List<IDrawable> list = new List<IDrawable>();
                list.AddRange(_LinkedDrawables);
                list.AddRange(_Drawables);
                foreach (IDrawable id in _Drawables)
                {
                    if (id is Container)
                    {
                        list.AddRange(((Container)id).LinkedDrawables);
                    }
                }
                return list;
            }
        }*/
        /// <summary>
        /// Generate the SBML view
        /// </summary>
        /// <param name="oInfo"></param>
        /// <returns></returns>
        public override SBMLExtension.EmlRenderExtension.LocalStyle ToStyle(SBMLExtension.EmlRenderExtension.RenderInformation oInfo)
        {

            SBMLExtension.EmlRenderExtension.LocalStyle oResult = new SBMLExtension.EmlRenderExtension.LocalStyle();
            SBMLExtension.EmlRenderExtension.Group oGroup = new SBMLExtension.EmlRenderExtension.Group();
            oGroup.Stroke = oInfo.AddColorDefinition(_oEdgeColor);
            oGroup.StrokeWidth = _dEdgeThickness.ToString();
            if (_oFillColorStart == _oFillColorEnd)
                oGroup.Fill = oInfo.AddColorDefinition(_oFillColorStart);
            else
                oGroup.Fill = oInfo.AddLinearGradientDefinition(_oFillColorStart, _oFillColorEnd);

            RectangleF oGlyphRect = Bounds;

            switch (Shape)
            {
                case SHAPE.Ellipse:

                    SBMLExtension.EmlRenderExtension.Ellipses newEllipses = new SBMLExtension.EmlRenderExtension.Ellipses();
                    newEllipses.CX = (oGlyphRect.Width / 2f).ToString();
                    newEllipses.CY = (oGlyphRect.Height / 2f).ToString();
                    newEllipses.RX = (oGlyphRect.Width / 2f).ToString();
                    newEllipses.RY = (oGlyphRect.Height / 2f).ToString();
                    oGroup.Children.Add(newEllipses);
                    break;
                case SHAPE.RoundedRectangle:
                    oGroup.Children.Add(new SBMLExtension.EmlRenderExtension.Rectangle("10%"));
                    break;
                case SHAPE.Octahedron:
                    {
                        float dOffset = Math.Min(Bounds.Width, Bounds.Height) * 0.10f;
                        RectangleF oTemp = Bounds;
                        oTemp.X = 0f;
                        oTemp.Y = 0f;

                        SBMLExtension.EmlRenderExtension.Polygon oPolygon = new SBMLExtension.EmlRenderExtension.Polygon();
                        oPolygon.Fill = oGroup.Fill;
                        oPolygon.Stroke = oGroup.Stroke;
                        oPolygon.StrokeWidth = oGroup.StrokeWidth;

                        oPolygon.CurveSegments.Add(new SBMLExtension.LayoutExtension.CurveSegment(
                            new SBMLExtension.LayoutExtension.Point(oTemp.X, oTemp.Y + dOffset),
                            new SBMLExtension.LayoutExtension.Point(oTemp.X + dOffset, oTemp.Y)
                            ));
                        oPolygon.CurveSegments.Add(new SBMLExtension.LayoutExtension.CurveSegment(
                            new SBMLExtension.LayoutExtension.Point(oTemp.X + dOffset, oTemp.Y),
                            new SBMLExtension.LayoutExtension.Point(oTemp.X + oTemp.Width - dOffset, oTemp.Y)
                            ));
                        oPolygon.CurveSegments.Add(new SBMLExtension.LayoutExtension.CurveSegment(
                            new SBMLExtension.LayoutExtension.Point(oTemp.X + oTemp.Width - dOffset, oTemp.Y),
                            new SBMLExtension.LayoutExtension.Point(oTemp.X + oTemp.Width, oTemp.Y + dOffset)
                            ));
                        oPolygon.CurveSegments.Add(new SBMLExtension.LayoutExtension.CurveSegment(
                            new SBMLExtension.LayoutExtension.Point(oTemp.X + oTemp.Width, oTemp.Y + dOffset),
                            new SBMLExtension.LayoutExtension.Point(oTemp.X + oTemp.Width, oTemp.Y + oTemp.Height - dOffset)
                            ));
                        oPolygon.CurveSegments.Add(new SBMLExtension.LayoutExtension.CurveSegment(
                            new SBMLExtension.LayoutExtension.Point(oTemp.X + oTemp.Width, oTemp.Y + oTemp.Height - dOffset),
                            new SBMLExtension.LayoutExtension.Point(oTemp.X + oTemp.Width - dOffset, oTemp.Y + oTemp.Height)
                            ));
                        oPolygon.CurveSegments.Add(new SBMLExtension.LayoutExtension.CurveSegment(
                            new SBMLExtension.LayoutExtension.Point(oTemp.X + oTemp.Width - dOffset, oTemp.Y + oTemp.Height),
                            new SBMLExtension.LayoutExtension.Point(oTemp.X + dOffset, oTemp.Y + oTemp.Height)
                            ));
                        oPolygon.CurveSegments.Add(new SBMLExtension.LayoutExtension.CurveSegment(
                            new SBMLExtension.LayoutExtension.Point(oTemp.X + dOffset, oTemp.Y + oTemp.Height),
                            new SBMLExtension.LayoutExtension.Point(oTemp.X, oTemp.Y + oTemp.Height - dOffset)
                            ));
                        oPolygon.CurveSegments.Add(new SBMLExtension.LayoutExtension.CurveSegment(
                            new SBMLExtension.LayoutExtension.Point(oTemp.X, oTemp.Y + oTemp.Height - dOffset),
                            new SBMLExtension.LayoutExtension.Point(oTemp.X, oTemp.Y + dOffset)
                            ));
                        oGroup.Children.Add(oPolygon);

                        if (ShowCloneMarker)
                        {
                            AddCloneMarkerGradient(oInfo);

                            SBMLExtension.EmlRenderExtension.Polygon oPolygon2 = new SBMLExtension.EmlRenderExtension.Polygon();
                            oPolygon2.Fill = "cloneMarker";


                            oPolygon2.CurveSegments.Add(new SBMLExtension.LayoutExtension.CurveSegment(
                                new SBMLExtension.LayoutExtension.Point(oTemp.X, oTemp.Y + dOffset),
                                new SBMLExtension.LayoutExtension.Point(oTemp.X + dOffset, oTemp.Y)
                                ));
                            oPolygon2.CurveSegments.Add(new SBMLExtension.LayoutExtension.CurveSegment(
                                new SBMLExtension.LayoutExtension.Point(oTemp.X + dOffset, oTemp.Y),
                                new SBMLExtension.LayoutExtension.Point(oTemp.X + oTemp.Width - dOffset, oTemp.Y)
                                ));
                            oPolygon2.CurveSegments.Add(new SBMLExtension.LayoutExtension.CurveSegment(
                                new SBMLExtension.LayoutExtension.Point(oTemp.X + oTemp.Width - dOffset, oTemp.Y),
                                new SBMLExtension.LayoutExtension.Point(oTemp.X + oTemp.Width, oTemp.Y + dOffset)
                                ));
                            oPolygon2.CurveSegments.Add(new SBMLExtension.LayoutExtension.CurveSegment(
                                new SBMLExtension.LayoutExtension.Point(oTemp.X + oTemp.Width, oTemp.Y + dOffset),
                                new SBMLExtension.LayoutExtension.Point(oTemp.X + oTemp.Width, oTemp.Y + oTemp.Height - dOffset)
                                ));
                            oPolygon2.CurveSegments.Add(new SBMLExtension.LayoutExtension.CurveSegment(
                                new SBMLExtension.LayoutExtension.Point(oTemp.X + oTemp.Width, oTemp.Y + oTemp.Height - dOffset),
                                new SBMLExtension.LayoutExtension.Point(oTemp.X + oTemp.Width - dOffset, oTemp.Y + oTemp.Height)
                                ));
                            oPolygon2.CurveSegments.Add(new SBMLExtension.LayoutExtension.CurveSegment(
                                new SBMLExtension.LayoutExtension.Point(oTemp.X + oTemp.Width - dOffset, oTemp.Y + oTemp.Height),
                                new SBMLExtension.LayoutExtension.Point(oTemp.X + dOffset, oTemp.Y + oTemp.Height)
                                ));
                            oPolygon2.CurveSegments.Add(new SBMLExtension.LayoutExtension.CurveSegment(
                                new SBMLExtension.LayoutExtension.Point(oTemp.X + dOffset, oTemp.Y + oTemp.Height),
                                new SBMLExtension.LayoutExtension.Point(oTemp.X, oTemp.Y + oTemp.Height - dOffset)
                                ));
                            oPolygon2.CurveSegments.Add(new SBMLExtension.LayoutExtension.CurveSegment(
                                new SBMLExtension.LayoutExtension.Point(oTemp.X, oTemp.Y + oTemp.Height - dOffset),
                                new SBMLExtension.LayoutExtension.Point(oTemp.X, oTemp.Y + dOffset)
                                ));
                            oGroup.Children.Add(oPolygon2);

                            AddCloneMarkerText(oInfo, oGroup);
                        }

                        break;
                    }
                default:
                case SHAPE.Box:
                    oGroup.Children.Add(new SBMLExtension.EmlRenderExtension.Rectangle());
                    break;
            }

            AddDecoratorsToGroup(oInfo, oGroup);

            AddLabelGlyph(oInfo, oGroup);

            oResult.Group = oGroup;
            return oResult;
        }

        public override void DoneModifying(Corner oCorner, bool modifyAnchoredItems)
        {
            base.DoneModifying(oCorner, modifyAnchoredItems);
            for (int i = _Drawables.Count - 1; i >= 0; --i)
            {
                if (_Drawables[i].IsVisible && !Bounds.Contains(_Drawables[i].Bounds))
                {
                    RemoveItem(_Drawables[i]);
                }
            }
        }

        /// <summary>
        /// Change the size of this container so that is will cover all of the 
        /// contained items
        /// </summary>
        public virtual void AdjustSize()
        {
            RectangleF bounds = Bounds;
            float xmin = bounds.Left, xmax = bounds.Right, ymin = bounds.Top, ymax = bounds.Bottom;
            foreach (IDrawable id in _Drawables)
            {
                if (id.IsVisible)
                {
                    RectangleF rect = id.Bounds;
                    if (rect.Left <= xmin + 10) xmin = rect.Left - 10;
                    if (rect.Top <= ymin + 10) ymin = rect.Top - 10;
                    if (rect.Right >= xmax - 10) xmax = rect.Right + 10;
                    if (rect.Bottom >= ymax - 10) ymax = rect.Bottom + 10;
                }
            }
            _Location = new PointF(xmin, ymin);
            _Dimensions = new SizeF(xmax - xmin, ymax - ymin);
        }

        /// <summary>
        /// Change the size of this container so that it will also include the given item
        /// </summary>
        public virtual void AdjustSize(IDrawable id)
        {
            RectangleF bounds = Bounds;
            float xmin = bounds.Left, xmax = bounds.Right, ymin = bounds.Top, ymax = bounds.Bottom;

            RectangleF rect = id.Bounds;
            if (!bounds.Contains(rect))
            {
                if (rect.Left <= xmin) xmin = rect.Left - 10;
                if (rect.Top <= ymin) ymin = rect.Top - 10;
                if (rect.Right >= xmax) xmax = rect.Right + 10;
                if (rect.Bottom >= ymax) ymax = rect.Bottom + 10;

                _Location = new PointF(xmin, ymin);
                _Dimensions = new SizeF(xmax - xmin, ymax - ymin);
            }
        }

        /// <summary>
        /// Sets the view for all the items in this container
        /// </summary>
        public override IDrawable.ViewType View
        {
            get
            {
                return base.View;
            }
            set
            {
                base.View = value;
                foreach (IDrawable id in _Drawables)
                    id.View = value;
            }
        }

        /// <summary>
        /// Sets all the container items' IsVisible as well
        /// </summary>
        public override bool IsVisible
        {
            get
            {
                return base.IsVisible;
            }
            set
            {
                base.IsVisible = value;
                foreach (IDrawable id in _Drawables)
                {
                    id.IsVisible = value;
                }
            }
        }

        public override void WriteXML(System.Xml.XmlWriter writer)
        {
            base.WriteXML(writer);
            writer.WriteStartElement("ContainerGlyph", "");

            writer.WriteStartElement("Shape", "");
            writer.WriteValue((int)(_oShape - SHAPE.Box));
            writer.WriteEndElement();

            if (_Drawables.Count > 0)
            {
                bool first = true;

                foreach (IDrawable id in _Drawables)
                {
                    if (id.NullItem && id.Original == null)
                    {
                        if (first)
                        {
                            writer.WriteStartElement("NullItems", "");
                            first = false;
                        }
                        IO.XMLWriter.WriteDrawable(id, this, writer);
                    }
                }

                if (!first)
                    writer.WriteEndElement();
            }

            writer.WriteEndElement();
        }

        public virtual void GenerateDrawablesFromItem()
        {
            if (_Item is Core.Container)
            {
                Core.Container container = (Core.Container)_Item;

                ContainerGlyph oldDrawable = _Item.Drawable as CompartmentGlyph;
                /*
                 if (Parent != null)
                    newDrawable = IO.XMLReader.ReadDrawable(reader, this, Parent.Drawable);
                 else
                    newDrawable = IO.XMLReader.ReadDrawable(reader, this, null);
                */
                ContainerGlyph newDrawable = this;
                if (oldDrawable != null)
                {
                    List<Objects.IDrawable> drawables = new List<Athena.Objects.IDrawable>();
                    drawables.AddRange(oldDrawable.Drawables);
                    foreach (Objects.IDrawable id in drawables)
                        id.Parent = newDrawable;
                }
                else
                {
                    foreach (Item item in container.LocalItems)
                    {
                        if (item.Drawable != null)
                        {
                            item.Drawable.Parent = newDrawable;
                            if (item.Drawable.Alias != null)
                            {
                                List<IDrawable> alias = item.Drawable.Alias;
                                foreach (Objects.IDrawable id in alias)
                                {
                                    id.Parent = newDrawable;
                                }
                            }
                        }
                    }
                }
                if (Model.Empty != null)
                {
                    foreach (Objects.IDrawable id in Model.Empty.Alias)
                    {
                        IGlyph ig = id as IGlyph;
                        if (ig != null)
                        {
                            List<Connector> incoming = ig.IncomingConnections,
                                            outgoing = ig.OutgoingConnections;
                            foreach (Connector c in incoming)
                                if (c.Parent == this)
                                    ig.Parent = this;
                            foreach (Connector c in outgoing)
                                if (c.Parent == this)
                                    ig.Parent = this;
                        }
                    }
                }
                /*
                if (container is Module)
                {
                    Module module = (Module)container;
                    Hashtable mappings = module.AllMappings;
                    if (mappings != null && mappings.Count > 0)
                    {
                        foreach (object o1 in mappings.Keys)
                        {
                            object o2 = mappings[o1];
                            Item i1 = o1 as Item, i2 = o2 as Item;
                            if (i1 != null && i1.Drawable != null && i2 != null && i2.Drawable != null)
                            {
                                AddItem(new ModuleConnector(i1.Drawable, i2.Drawable));                                
                            }
                        }
                    }
                }*/
            }
        }

        public override bool ReadXML(XmlReader reader)
        {
            base.ReadXML(reader);

            GenerateDrawablesFromItem();

            bool read = reader.Read();

            if (reader.NodeType == XmlNodeType.Element && reader.Name == "ContainerGlyph")
            {
                read = reader.Read();
                while (read)
                {
                    if (reader.NodeType == XmlNodeType.EndElement)
                        break;

                    if (reader.NodeType == XmlNodeType.Element)
                    {
                        switch (reader.Name)
                        {
                            case "Shape":
                                read = reader.Read();
                                if (reader.NodeType == XmlNodeType.Text)
                                    _oShape = SHAPE.Box + reader.ReadContentAsInt();

                                break;
                            case "NullItems":
                                while (read)
                                {
                                    IDrawable item = IO.XMLReader.ReadDrawable(reader, null, this);
                                    if (item == null)
                                        break;
                                    else
                                        AddItem(item, false);
                                    //read = reader.Read();
                                }
                                break;
                            default:
                                while (reader.NodeType != XmlNodeType.EndElement)
                                    read = reader.Read();
                                break;
                        }
                    }
                    if (!read || (reader.NodeType == XmlNodeType.EndElement && reader.Name == "ContainerGlyph")) break;

                    if (reader.NodeType != XmlNodeType.Element)
                        read = reader.Read();
                }
                //DoneModifying(Corner.None, false);
                return true;
            }
            return false;
        }

        public override void LoadDefaults(IDrawable drawable)
        {
            SpeciesGlyph Default = drawable as SpeciesGlyph;
            if (Default != null)
            {
                base.LoadDefaults(drawable);
                Shape = DEFAULT.Shape;
            }
        }

        public static ContainerGlyph DEFAULT = null;
    }
}
